/**
 * 
 */
package ro.dta.idbi.ui.patterns;

import java.awt.Point;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.event.EventListenerList;

import ro.dta.idbi.interfaces.sql.ISqlColumn;
import ro.dta.idbi.interfaces.sql.ISqlTable;
import ro.dta.idbi.ui.sql.SqlColumnCell;

import com.mxgraph.model.mxCell;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxEvent;
import com.mxgraph.util.mxEventObject;
import com.mxgraph.util.mxEventSource.mxIEventListener;

/**
 * Normalisation pattern graph component
 * 
 * @author Tudor Dobrila
 * 
 */
public class NormaliseGraphComp extends mxGraphComponent {

	private static final long serialVersionUID = -195474195401249673L;

	private static final Logger LOGGER = Logger
			.getLogger("com.dta.idbi.patterns.ui.NormaliseGraphComp");

	private final transient NormaliseGraph graph;

	private final transient ISqlTable leftTable;

	private final transient ISqlTable rightTable;

	private final transient ISqlColumn addDetCol;

	private final transient ISqlColumn addDepCol;

	private final transient EventListenerList listenerList = new javax.swing.event.EventListenerList();

	/**
	 * Normalisation graph component
	 * 
	 * @param graph
	 *            Graph
	 * @param leftTable
	 *            Original table (before normalisation)
	 * @param rightTable
	 *            New table
	 * @param addDetCol
	 *            Add determinant column in the right table
	 * @param addDepCol
	 *            Add dependent column in the right table
	 */
	public NormaliseGraphComp(final NormaliseGraph graph, final ISqlTable leftTable,
			final ISqlTable rightTable, final ISqlColumn addDetCol, final ISqlColumn addDepCol) {
		super(graph);

		this.graph = graph;
		this.leftTable = leftTable;
		this.rightTable = rightTable;
		this.addDetCol = addDetCol;
		this.addDepCol = addDepCol;

		setEnterStopsCellEditing(true);
		setConnectable(false);

		graph.addListener(mxEvent.MOVE_CELLS, new mxIEventListener() {

			public void invoke(final Object sender, final mxEventObject evt) {
				final mxCell dropCell = graph.getDropCell();
				final Point dropLocation = graph.getDropLocation();

				if (dropCell != null && dropLocation != null) {
					final mxCell targetCell = (mxCell) getCellAt(dropLocation.x, dropLocation.y);
					if (targetCell != null
							&& (targetCell.getValue().equals("drop determinant") || targetCell
									.getValue().equals("drop dependent"))) {
						if (targetCell.getValue().equals("drop determinant")) {
							addDeterminant(dropCell);
						}

						if (targetCell.getValue().equals("drop dependent")) {
							addDependent(dropCell);
						}
					} else {
						if (dropCell.getStyle().equals("COLUMN")) {
							graph.getModel().beginUpdate();
							graph.getModel().remove(dropCell);
							graph.getModel().endUpdate();
							leftTable.relayout(graph);
						}
					}
				}

				graph.refresh();
			}
		});
	}

	/**
	 * Add a dependent column to the right table
	 */
	protected void addDependent(final mxCell column) {
		graph.getModel().beginUpdate();

		try {
			if (column instanceof SqlColumnCell) {
				if ("PRIMARYKEY".equals(column.getStyle())
						|| rightTable.columnNameExists(column.getValue().toString())) {
					graph.getModel().remove(column);
					leftTable.relayout(graph);
				} else {
					final SqlColumnCell col = (SqlColumnCell) column;
					final SqlColumnCell newCol = (SqlColumnCell) col.clone();
					newCol.setStyle("DEPENDENT");

					graph.getModel().remove(column);

					leftTable.removeColumn(col);
					leftTable.relayout(graph);
					rightTable.addBeforeColumn(addDepCol, newCol);
					rightTable.relayout(graph);
					fireNormaliseEvent(new NormaliseEvent(this));
				}
			}
		} catch (CloneNotSupportedException e) {
			LOGGER.log(Level.SEVERE, "Fatal exception", e);
		} finally {
			graph.getModel().endUpdate();
		}
	}

	/**
	 * Add a determinant column on the right table
	 */
	protected void addDeterminant(final mxCell column) {
		graph.getModel().beginUpdate();

		try {
			if (column instanceof SqlColumnCell) {
				final SqlColumnCell col = (SqlColumnCell) column;
				final SqlColumnCell newCol = (SqlColumnCell) col.clone();
				newCol.setStyle("DETERMINANT");

				graph.getModel().remove(column);
				graph.insertEdge(graph.getDefaultParent(), "", "", column, newCol);

				leftTable.relayout(graph);
				rightTable.addBeforeColumn(addDetCol, newCol);
				rightTable.relayout(graph);
				fireNormaliseEvent(new NormaliseEvent(this));
			}
		} catch (CloneNotSupportedException e) {
			LOGGER.log(Level.SEVERE, "Fatal exception", e);
		} finally {
			graph.setAutoSizeCells(true);
			graph.getModel().endUpdate();
		}
	}

	public void addNormaliseEventListener(final NormaliseListener listener) {
		listenerList.add(NormaliseListener.class, listener);
	}

	public void removeNormaliseEventListener(final NormaliseListener listener) {
		listenerList.remove(NormaliseListener.class, listener);
	}

	private void fireNormaliseEvent(final NormaliseEvent evt) {
		final Object[] listeners = listenerList.getListenerList();

		for (int i = 0; i < listeners.length; i += 2) {
			if (listeners[i] == NormaliseListener.class) {
				((NormaliseListener) listeners[i + 1]).newTableChanged(evt);
			}
		}
	}
}
